/*
 * BlackLightning.BLSGE
 * Andy Tidball
 * 
 * Project: Black Lightning
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using BlackLightning.PluginLib;
using BlackLightning.PluginLib.LoginClients;

// tag BLSGE as a login client
[assembly: BLLoginClient(typeof(BLSGE))]

namespace BlackLightning.PluginLib.LoginClients {
	/// <summary>
	/// An SGE-like LoginClient interface for logging into Simu games.
	/// </summary>
	internal class BLSGE : LoginClient {
		// constants
		private const string HelpText = "The default login interface for Black Lightning.";

		// constants - options
		private const string SaveLoginInfoOptionName     = "Save Login Information";
		private const bool   SaveLoginInfoOptionDefault  = true;
		private const bool   SaveLoginInfoOptionHidden   = true;
		private const string UsernameOptionName          = "Username";
		private const string UsernameOptionDefault       = "";
		private const bool   UsernameOptionHidden        = true;
		private const string PasswordOptionName          = "Password";
		private const string PasswordOptionDefault       = "";
		private const bool   PasswordOptionHidden        = true;
		private const string PasswordOptionEncrypt       = "8It`RVy{?Df~SHS.1mUS8|(@u0+5JM' %=wim@J U|uO6J%7K0u4qD.[CIF5tZ~";
		private const string ShowAvailGamesOptionName    = "Show Only Available Games";
		private const bool   ShowAvailGamesOptionDefault = true;
		private const bool   ShowAvailGamesOptionHidden  = true;
		private const string LastGameOptionName          = "Last Game Played";
		private const string LastGameOptionDefault       = "";
		private const bool   LastGameOptionHidden        = true;
		private const string LastCharOptionName          = "Last Character Played";
		private const string LastCharOptionDefault       = "";
		private const bool   LastCharOptionHidden        = true;
		private const string LastFEOptionName            = "Last Front End Used";
		private const string LastFEOptionDefault         = "STORM";
		private const bool   LastFEOptionHidden          = true;

		/// <summary>
		/// Represents a single state that the dialog is in.
		/// </summary>
		private enum State {
			/// <summary>
			/// The dialog is currently displaying the login panel.
			/// </summary>
			Login,

			/// <summary>
			/// The dialog is currently displaying the game choice panel.
			/// </summary>
			Game,

			/// <summary>
			/// The dialog is currently displaying the character choice panel.
			/// </summary>
			Character
		}

		// member data
		private SalFile        _SalFile      = null; // the SAL file retrieved from the login server
		private State          _State;               // the current state of the dialog
		private List<GameInfo> _Games        = null; // the list of available games from the server
		private GameInfo       _SelectedGame = null; // the game that the user has selected to play
		private CharacterList  _Chars        = null; // the list of available characters from the server
		private string         _SelectedChar = null; // the code of the character that the user has selected to play
		private string         _SelectedFE   = null; // the code of the front end that the user has selected to play with

		// member data - options
		private bool   _SaveLoginInformation;
		private string _StoredUsername;
		private string _StoredPassword;
		private bool   _ShowAvailGames;
		private string _LastGame;
		private string _LastChar;
		private string _LastFE;

		// member data - main controls
		private Button    btnBack;
		private Button    btnNext;
		private StatusBar stbStatus;

		// member data - login panel
		private Panel     pnlLogin;
		private Label     lblLoginTitle;
		private Label     lblUsername;
		private Label     lblPassword;
		private TextBox   txtUsername;
		private TextBox   txtPassword;
		private Button    btnOptions;
		private CheckBox  chbRemember;
		private LinkLabel lblTerms;

		// member data - game panel
		private Panel        pnlGame;
		private Label        lblGameTitle;
		private Label        lblAccountName;
		private ListView     lsvGames;
		private ColumnHeader lscGameCode;
		private ColumnHeader lscGameName;
		private ColumnHeader lscGameAccess;
		private CheckBox     chbAllowedGames;

		// member data - character panel
		private Panel        pnlCharacter;
		private Label        lblCharTitle;
		private ListView     lsvChars;
		private ColumnHeader lscCharCode;
		private ColumnHeader lscCharName;
		private Label        lblGameName;
		private Label        lblCharSlots;
		private RadioButton  rdbStormFront;
		private RadioButton  rdbWizard;

		/// <summary>
		/// Constructs a new BLSGE.
		/// </summary>
		public BLSGE() {
			// create our controls
			InitializeControls();

			// goto the initial login state
			ChangeState(State.Login, true, true);
		}

		/// <summary>
		/// Called whenever Black Lightning has a private message for us.
		/// </summary>
		/// <param name="Message">The message sent by Black Lightning.</param>
		public override void HandleMessage(global::BlackLightning.PluginLib.Message Message) {
			if (Message is LoadedMessage) {
				HandleMessage_Loaded(Message as LoadedMessage);
			} else if (Message is UnloadedMessage) {
				HandleMessage_Unloaded(Message as UnloadedMessage);
			} else if (Message is OptionValueChangedMessage) {
				HandleMessage_OptionValueChanged(Message as OptionValueChangedMessage);
			}
		}

		/// <summary>
		/// Called whenever Black Lightning sends us a LoadedMessage.
		/// </summary>
		/// <param name="Message">The loaded message.</param>
		public void HandleMessage_Loaded(LoadedMessage Message) {
			// register our options
			base.RegisterOption(SaveLoginInfoOptionName,  typeof(bool),   SaveLoginInfoOptionDefault,  null, null, SaveLoginInfoOptionHidden);
			base.RegisterOption(UsernameOptionName,       typeof(string), UsernameOptionDefault,       null, null, UsernameOptionHidden);
			base.RegisterOption(PasswordOptionName,       typeof(string), PasswordOptionDefault,       null, null, PasswordOptionHidden, false, PasswordOptionEncrypt);
			base.RegisterOption(ShowAvailGamesOptionName, typeof(bool),   ShowAvailGamesOptionDefault, null, null, ShowAvailGamesOptionHidden);
			base.RegisterOption(LastGameOptionName,       typeof(string), LastGameOptionDefault,       null, null, LastGameOptionHidden);
			base.RegisterOption(LastCharOptionName,       typeof(string), LastCharOptionDefault,       null, null, LastCharOptionHidden);
			base.RegisterOption(LastFEOptionName,         typeof(string), LastFEOptionDefault,         null, null, LastFEOptionHidden);
			
			base.RegisterOption("Color",   typeof(Color), Color.Blue);
			base.RegisterOption("Boolean", typeof(bool),  false);
			base.RegisterOption("Point",   typeof(Point), new Point(0,0));
			base.RegisterOption("Size",    typeof(Size),  new Size(0,0));

			// display the option's values in the controls
			chbRemember.Checked     = _SaveLoginInformation;
			txtUsername.Text        = _StoredUsername;
			txtPassword.Text        = _StoredPassword;
			chbAllowedGames.Checked = _ShowAvailGames;
			rdbStormFront.Checked   = (_LastFE == "STORM");
			rdbWizard.Checked       = (_LastFE == "WIZ");
		}

		/// <summary>
		/// Called whenever Black Lightning sends us an UnloadedMessage.
		/// </summary>
		/// <param name="Message">The unloaded message.</param>
		public void HandleMessage_Unloaded(UnloadedMessage Message) {
			// if they don't want to store the login information, clear out the values in Black Lightning
			if (!chbRemember.Checked) {
				base.ChangeOptionValue(UsernameOptionName, "");
				base.ChangeOptionValue(PasswordOptionName, "");
			}
		}

		/// <summary>
		/// Called whenever Black Lightning sends us an OptionValueChangedMessage.
		/// </summary>
		/// <param name="Message">The option value changed message.</param>
		public void HandleMessage_OptionValueChanged(OptionValueChangedMessage Message) {
			if (Message.Name == SaveLoginInfoOptionName) {
				_SaveLoginInformation = (bool)Message.NewValue;
			} else if (Message.Name == UsernameOptionName) {
				_StoredUsername = Message.NewValue as string;
			} else if (Message.Name == PasswordOptionName) {
				_StoredPassword = Message.NewValue as string;
			} else if (Message.Name == ShowAvailGamesOptionName) {
				_ShowAvailGames = (bool)Message.NewValue;
			} else if (Message.Name == LastGameOptionName) {
				_LastGame = Message.NewValue as string;
			} else if (Message.Name == LastCharOptionName) {
				_LastChar = Message.NewValue as string;
			} else if (Message.Name == LastFEOptionName) {
				_LastFE = Message.NewValue as string;
			}
		}

		/// <summary>
		/// Gets a property of the BLSGE.
		/// </summary>
		/// <param name="Property">The value of the property to retrieve.</param>
		/// <returns>The value of the given property.</returns>
		public override object GetProperty(LoginClientProperty Property) {
			switch (Property) {
				case LoginClientProperty.HelpText: return HelpText;
				case LoginClientProperty.Username: return txtUsername.Text;
				case LoginClientProperty.Password: return txtPassword.Text;
				case LoginClientProperty.GameCode: return _SelectedGame.Code;
				case LoginClientProperty.CharCode: return _SelectedChar;
				case LoginClientProperty.FECode:   return _SelectedFE;
				default:                           return null;
			}
		}

		/// <summary>
		/// Provides the login logic for this LoginClient implementation.
		/// </summary>
		/// <param name="Args">The command-line arguments passed to Black Lightning for use logging in.</param>
		/// <returns>The SAL file returned from the login server.</returns>
		public override SalFile Login(string[] Args) {
			// show the dialog
			if (ShowDialog() != DialogResult.OK) {
				return null;
			}
			
			// return the SAL file we grabbed
			return _SalFile;
		}

		/// <summary>
		/// Creates and initializes all of our child controls.
		/// </summary>
		private void InitializeControls() {
			// main controls
			btnBack   = new Button();
			btnNext   = new Button();
			stbStatus = new StatusBar();

			// login panel
			pnlLogin      = new Panel();
			lblLoginTitle = new Label();
			lblUsername   = new Label();
			lblPassword   = new Label();
			txtUsername   = new TextBox();
			txtPassword   = new TextBox();
			btnOptions   = new Button();
			chbRemember   = new CheckBox();
			lblTerms      = new LinkLabel();

			// game panel
			pnlGame         = new Panel();
			lblGameTitle    = new Label();
			lblAccountName  = new Label();
			lsvGames        = new ListView();
			lscGameCode     = new ColumnHeader();
			lscGameName     = new ColumnHeader();
			lscGameAccess   = new ColumnHeader();
			chbAllowedGames = new CheckBox();

			// character panel
			pnlCharacter  = new Panel();
			lblCharTitle  = new Label();
			lblGameName   = new Label();
			lblCharSlots  = new Label();
			lsvChars      = new ListView();
			lscCharName   = new ColumnHeader();
			lscCharCode   = new ColumnHeader();
			rdbStormFront = new RadioButton();
			rdbWizard     = new RadioButton();

			// suspend layout until everything is placed
			pnlLogin.SuspendLayout();
			pnlGame.SuspendLayout();
			pnlCharacter.SuspendLayout();
			SuspendLayout();

			// btnBack
			btnBack.Enabled = false;
			btnBack.FlatStyle = FlatStyle.Flat;
			btnBack.Location = new Point(8, 252);
			btnBack.Name = "btnBack";
			btnBack.TabIndex = 4;
			btnBack.Text = "<< Back";
			btnBack.Click += btnBack_Click;
			// btnNext
			btnNext.FlatStyle = FlatStyle.Flat;
			btnNext.Location = new Point(208, 252);
			btnNext.Name = "btnNext";
			btnNext.TabIndex = 5;
			btnNext.Text = "Next >>";
			btnNext.Click += btnNext_Click;
			// stbStatus
			stbStatus.Location = new Point(0, 281);
			stbStatus.Name = "stbStatus";
			stbStatus.Size = new Size(292, 22);
			stbStatus.SizingGrip = false;
			stbStatus.Text = "Welcome to Black Lightning";

			// pnlLogin
			pnlLogin.Location = new Point(0, 0);
			pnlLogin.Name = "pnlLogin";
			pnlLogin.Size = new Size(292, 248);
			pnlLogin.TabIndex = 1;
			pnlLogin.Visible = true;
			pnlLogin.Controls.Add(lblLoginTitle);
			pnlLogin.Controls.Add(lblUsername);
			pnlLogin.Controls.Add(txtUsername);
			pnlLogin.Controls.Add(lblPassword);
			pnlLogin.Controls.Add(txtPassword);
			pnlLogin.Controls.Add(btnOptions);
			pnlLogin.Controls.Add(chbRemember);
			pnlLogin.Controls.Add(lblTerms);
			// lblLoginTitle
			lblLoginTitle.Font = new Font("Microsoft Sans Serif", 24F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblLoginTitle.Location = new Point(8, 8);
			lblLoginTitle.Name = "lblLoginTitle";
			lblLoginTitle.Size = new Size(276, 40);
			lblLoginTitle.Text = "Login to Play.Net";
			lblLoginTitle.TextAlign = ContentAlignment.MiddleCenter;
			// lblUsername
			lblUsername.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Bold, GraphicsUnit.Point, ((System.Byte)(0)));
			lblUsername.Location = new Point(16, 64);
			lblUsername.Name = "lblUsername";
			lblUsername.Size = new Size(96, 26);
			lblUsername.Text = "Username:";
			lblUsername.TextAlign = ContentAlignment.MiddleLeft;
			// lblPassword
			lblPassword.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Bold, GraphicsUnit.Point, ((System.Byte)(0)));
			lblPassword.Location = new Point(16, 104);
			lblPassword.Name = "lblPassword";
			lblPassword.Size = new Size(96, 26);
			lblPassword.Text = "Password:";
			lblPassword.TextAlign = ContentAlignment.MiddleLeft;
			// txtUsername
			txtUsername.BorderStyle = BorderStyle.FixedSingle;
			txtUsername.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Bold, GraphicsUnit.Point, ((System.Byte)(0)));
			txtUsername.Location = new Point(112, 64);
			txtUsername.Name = "txtUsername";
			txtUsername.Size = new Size(168, 26);
			txtUsername.TabIndex = 1;
			txtUsername.Text = "";
			txtUsername.TextChanged += txtUsername_TextChanged;
			// txtPassword
			txtPassword.BorderStyle = BorderStyle.FixedSingle;
			txtPassword.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Bold, GraphicsUnit.Point, ((System.Byte)(0)));
			txtPassword.Location = new Point(112, 104);
			txtPassword.Name = "txtPassword";
			txtPassword.PasswordChar = '*';
			txtPassword.Size = new Size(168, 26);
			txtPassword.TabIndex = 2;
			txtPassword.Text = "";
			txtPassword.TextChanged += txtPassword_TextChanged;
			// btnOptions
			//btnOptions.BackColor = Color.Red; // our default is no launcher path.. if set the color will be rectified
			btnOptions.FlatStyle = FlatStyle.Flat;
			btnOptions.Location = new Point(16, 144);
			btnOptions.Name = "btnOptions";
			btnOptions.TabIndex = 3;
			btnOptions.Text = "Options";
			btnOptions.Click += btnOptions_Click;
			// chbRemember
			chbRemember.FlatStyle = FlatStyle.Flat;
			chbRemember.Font = new Font("Microsoft Sans Serif", 9.75F, FontStyle.Bold, GraphicsUnit.Point, ((System.Byte)(0)));
			chbRemember.Location = new Point(112, 144);
			chbRemember.Name = "chbRemember";
			chbRemember.Size = new Size(172, 24);
			chbRemember.TabIndex = 4;
			chbRemember.Text = "Save Login Information";
			chbRemember.CheckedChanged += chbRemember_CheckedChanged;
			// lblTerms
			lblTerms.LinkArea = new LinkArea(133, 46);
			lblTerms.Location = new Point(16, 178);
			lblTerms.Name = "lblTerms";
			lblTerms.Size = new Size(264, 56);
			lblTerms.TabIndex = 5;
			lblTerms.TabStop = true;
			lblTerms.Text = "By entering your name and password, you\'re accepting the Simutronics Terms and Conditions.  These terms are available for viewing at http://www.play.net/playdotnet/simu_policy.asp";
			lblTerms.TextAlign = ContentAlignment.BottomLeft;
			lblTerms.LinkClicked += lblTerms_LinkClicked;

			// pnlGame
			pnlGame.Dock = DockStyle.Top;
			pnlGame.Location = new Point(0, 0);
			pnlGame.Name = "pnlGame";
			pnlGame.Size = new Size(292, 248);
			pnlGame.TabIndex = 2;
			pnlGame.Visible = false;
			pnlGame.Controls.Add(lblGameTitle);
			pnlGame.Controls.Add(lblAccountName);
			pnlGame.Controls.Add(lsvGames);
			pnlGame.Controls.Add(chbAllowedGames);
			// lblGameTitle
			lblGameTitle.Font = new Font("Microsoft Sans Serif", 24F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblGameTitle.Location = new Point(16, 8);
			lblGameTitle.Name = "lblGameTitle";
			lblGameTitle.Size = new Size(264, 40);
			lblGameTitle.Text = "Select Game";
			lblGameTitle.TextAlign = ContentAlignment.MiddleCenter;
			// lblAccountName
			lblAccountName.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblAccountName.Location = new Point(8, 44);
			lblAccountName.Name = "lblAccountName";
			lblAccountName.Size = new Size(276, 28);
			lblAccountName.Text = "";
			lblAccountName.TextAlign = ContentAlignment.MiddleCenter;
			// lsvGames
			lsvGames.Activation = ItemActivation.Standard;
			lsvGames.BorderStyle = BorderStyle.FixedSingle;
			lsvGames.Columns.AddRange(new ColumnHeader[] { lscGameCode, lscGameName, lscGameAccess });
			lsvGames.FullRowSelect = true;
			lsvGames.HeaderStyle = ColumnHeaderStyle.Nonclickable;
			lsvGames.HideSelection = false;
			lsvGames.HoverSelection = false;
			lsvGames.LabelWrap = false;
			lsvGames.Location = new Point(8, 72);
			lsvGames.MultiSelect = false;
			lsvGames.Name = "lsvGames";
			lsvGames.Size = new Size(276, 152);
			lsvGames.Sorting = SortOrder.Ascending;
			lsvGames.TabIndex = 1;
			lsvGames.View = View.Details;
			lsvGames.ItemActivate += lsvGames_ItemActivate;
			// lscGameCode
			lscGameCode.Text = "Code";
			lscGameCode.Width = 40;
			// lscGameName
			lscGameName.Text = "Name";
			lscGameName.Width = 160;
			// lscGameAccess
			lscGameAccess.Text = "Access";
			lscGameAccess.Width = 60;
			// chbAllowedGames
			chbAllowedGames.FlatStyle = FlatStyle.Flat;
			chbAllowedGames.Location = new Point(8, 224);
			chbAllowedGames.Name = "chbAllowedGames";
			chbAllowedGames.Size = new Size(276, 24);
			chbAllowedGames.TabIndex = 2;
			chbAllowedGames.Text = "Only display games that my account can access";
			chbAllowedGames.CheckedChanged += chbAllowedGames_CheckedChanged;

			// pnlCharacter
			pnlCharacter.Location = new Point(0, 0);
			pnlCharacter.Name = "pnlCharacter";
			pnlCharacter.Size = new Size(292, 248);
			pnlCharacter.TabIndex = 3;
			pnlCharacter.Visible = false;
			pnlCharacter.Controls.Add(lblCharTitle);
			pnlCharacter.Controls.Add(lblGameName);
			pnlCharacter.Controls.Add(lblCharSlots);
			pnlCharacter.Controls.Add(lsvChars);
			pnlCharacter.Controls.Add(rdbStormFront);
			pnlCharacter.Controls.Add(rdbWizard);
			// lblCharTitle
			lblCharTitle.Font = new Font("Microsoft Sans Serif", 24F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblCharTitle.Location = new Point(8, 8);
			lblCharTitle.Name = "lblCharTitle";
			lblCharTitle.Size = new Size(276, 40);
			lblCharTitle.Text = "Select Character";
			lblCharTitle.TextAlign = ContentAlignment.MiddleCenter;
			// lblGameName
			lblGameName.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblGameName.Location = new Point(8, 44);
			lblGameName.Name = "lblAccess";
			lblGameName.Size = new Size(226, 28);
			lblGameName.Text = "Access:";
			lblGameName.TextAlign = ContentAlignment.MiddleLeft;
			// lblCharSlots
			lblCharSlots.Font = new Font("Microsoft Sans Serif", 12F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblCharSlots.Location = new Point(234, 44);
			lblCharSlots.Name = "lblCharSlots";
			lblCharSlots.Size = new Size(50, 28);
			lblCharSlots.Text = "Slots Used:";
			lblCharSlots.TextAlign = ContentAlignment.MiddleRight;
			// lsvChars
			lsvChars.Activation = ItemActivation.Standard;
			lsvChars.BorderStyle = BorderStyle.FixedSingle;
			lsvChars.Columns.AddRange(new ColumnHeader[] { lscCharName, lscCharCode });
			lsvChars.FullRowSelect = true;
			lsvChars.HeaderStyle = ColumnHeaderStyle.Nonclickable;
			lsvChars.HideSelection = false;
			lsvChars.LabelWrap = false;
			lsvChars.Location = new Point(8, 72);
			lsvChars.MultiSelect = false;
			lsvChars.Name = "lsvChars";
			lsvChars.Size = new Size(276, 152);
			lsvChars.Sorting = SortOrder.Ascending;
			lsvChars.TabIndex = 1;
			lsvChars.View = View.Details;
			lsvChars.ItemActivate += lsvChars_ItemActivate;
			// lscCharCode
			lscCharCode.Text = "Code";
			lscCharCode.Width = 130;
			// lscCharName
			lscCharName.Text = "Name";
			lscCharName.Width = 130;
			// rdbStormFront
			rdbStormFront.Checked  = true;
			rdbStormFront.Location = new Point(56, 230);
			rdbStormFront.Name     = "rdbStormFront";
			rdbStormFront.Size     = new Size(90, 16);
			rdbStormFront.Text     = "StormFront";
			// rdbWizard
			rdbWizard.Location = new Point(162, 230);
			rdbWizard.Name     = "rdbWizard";
			rdbWizard.Size     = new Size(90, 16);
			rdbWizard.Text     = "Wizard";

			// LoginWindow
			AcceptButton = btnNext;
			AutoScaleDimensions = new Size(5, 13);
			BackColor = SystemColors.Control;
			//CancelButton = btnBack;
			ClientSize = new Size(292, 303);
			DialogResult = DialogResult.None;
			FormBorderStyle = FormBorderStyle.FixedSingle;
			MaximizeBox = false;
			Name = "BLSGE";
			Text = "Black Lightning";
			Controls.Add(btnBack);
			Controls.Add(btnNext);
			Controls.Add(stbStatus);
			Controls.Add(pnlLogin);
			Controls.Add(pnlGame);
			Controls.Add(pnlCharacter);
			Closed += this_Closed;

			// okay, perform layout
			pnlLogin.ResumeLayout(false);
			pnlGame.ResumeLayout(false);
			pnlCharacter.ResumeLayout(false);
			ResumeLayout(false);
		}

		/// <summary>
		/// Sets the text to display in the status bar.
		/// </summary>
		/// <param name="Status">The text to display in the status bar.</param>
		private void SetStatus(string Status) {
			stbStatus.Text = Status;
		}

		/// <summary>
		/// Enters or leaves the given state.  This method should only be called by GotoState().
		/// </summary>
		/// <param name="S">The state to enter or leave.</param>
		/// <param name="Enter">True to enter the state, false to leave it.</param>
		/// <param name="DefaultMessage">If Enter is true, this is whether or not to reset the status bar to the state's default message.</param>
		private void ChangeState(State S, bool Enter, bool DefaultMessage) {
			// set enabled and visible states appropriately
			switch (S) {
				case State.Login: {
					btnBack.Enabled     = !Enter;
					btnNext.Enabled     = Enter;
					txtUsername.Enabled = Enter;
					txtPassword.Enabled = Enter;
					btnOptions.Enabled  = Enter;
					chbRemember.Enabled = Enter;
					pnlLogin.Visible    = Enter;
					if (Enter) {
						txtUsername.Focus();
						if (DefaultMessage) {
							SetStatus("Enter your username and password.");
						}
					}
					break;
				}
				case State.Game: {
					btnBack.Enabled         = Enter;
					btnNext.Enabled         = Enter;
					lsvGames.Enabled        = Enter;
					chbAllowedGames.Enabled = Enter;
					pnlGame.Visible         = Enter;
					if (Enter) {
						lsvGames.Focus();
						if (DefaultMessage) {
							SetStatus("Select the game you want to play.");
						}
					}
					break;
				}
				case State.Character: {
					btnBack.Enabled      = Enter;
					btnNext.Enabled      = Enter;
					lsvChars.Enabled     = Enter;
					pnlCharacter.Visible = Enter;
					if (Enter) {
						lsvChars.Focus();
						if (DefaultMessage) {
							SetStatus("Select the character you want to play.");
						}
					}
					break;
				}
			}

			// if we're entering the state, record it as our current state
			if (Enter) {
				_State = S;
			}

			// use the wait cursor between states
			Cursor.Current = Enter ? Cursors.Default : Cursors.WaitCursor;
		}

		/// <summary>
		/// Called when the dialog is closed.
		/// </summary>
		/// <param name="Sender">The Form that was closed.</param>
		/// <param name="e">No arguments.</param>
		private void this_Closed(object Sender, EventArgs e) {
			if (Connected) {
				SetStatus("Disconnecting...");
				Disconnect();
			}
		}

		/// <summary>
		/// Called when the Back button is clicked.
		/// </summary>
		/// <param name="Sender">The button that was clicked.</param>
		/// <param name="e">No arguments.</param>
		private void btnBack_Click(object Sender, EventArgs e) {
			switch (_State) {
				case State.Game:      Back_Game();      break;
				case State.Character: Back_Character(); break;
			}
		}

		/// <summary>
		/// Called when the Next button is clicked.
		/// </summary>
		/// <param name="Sender">The button that was clicked.</param>
		/// <param name="e">No arguments.</param>
		private void btnNext_Click(object Sender, EventArgs e) {
			switch (_State) {
				case State.Login:     Next_Login();     break;
				case State.Game:      Next_Game();      break;
				case State.Character: Next_Character(); break;
			}
		}

		/// <summary>
		/// Called when the text of the Username box changes.
		/// </summary>
		/// <param name="Sender">The text box whose text was changed.</param>
		/// <param name="e">No arguments.</param>
		private void txtUsername_TextChanged(object Sender, EventArgs e) {
			// inform Black Lightning of the value change
			base.ChangeOptionValue(UsernameOptionName, txtUsername.Text);
		}

		/// <summary>
		/// Called when the text of the Password box changes.
		/// </summary>
		/// <param name="Sender">The text box whose text was changed.</param>
		/// <param name="e">No arguments.</param>
		private void txtPassword_TextChanged(object Sender, EventArgs e) {
			// inform Black Lightning of the value change
			base.ChangeOptionValue(PasswordOptionName, txtPassword.Text);
		}

		/// <summary>
		/// Called when the Options button is clicked.
		/// </summary>
		/// <param name="Sender">The button that was clicked.</param>
		/// <param name="e">No arguments.</param>
		private void btnOptions_Click(object Sender, EventArgs e) {
			// show the options dialog
			ShowOptions();

			// display the new values in the controls
			chbRemember.Checked     = _SaveLoginInformation;
			txtUsername.Text        = _StoredUsername;
			txtPassword.Text        = _StoredPassword;
			chbAllowedGames.Checked = _ShowAvailGames;
		}

		/// <summary>
		/// Called when the checked state of the chbRemember box is changed.
		/// </summary>
		/// <param name="Sender">The checkbox whose checked state changed.</param>
		/// <param name="e">No arguments.</param>
		private void chbRemember_CheckedChanged(object Sender, EventArgs e) {
			// inform Black Lightning of the value change
			base.ChangeOptionValue(SaveLoginInfoOptionName, chbRemember.Checked);
		}

		/// <summary>
		/// Called when the link in the terms is clicked.
		/// </summary>
		/// <param name="Sender">The LinkLabel that was clicked.</param>
		/// <param name="e">No arguments.</param>
		private void lblTerms_LinkClicked(object Sender, EventArgs e) {
			// run the terms web site
			System.Diagnostics.Process.Start(LoginClient.TermsURL);
		}

		/// <summary>
		/// Called to initiate the change from the Login state to the Game state.
		/// </summary>
		private void Next_Login() {
			// leave the login state
			ChangeState(State.Login, false, false);

			// if we're already connected and have a list of games, jump straight to the next state
			if (_Games != null && Connected) {
				ChangeState(State.Game, true, true);
				return;
			}

			// connect to the login server
			SetStatus("Connecting to " + LoginClient.LoginServerHost + ":" + LoginClient.LoginServerPort + "...");
			if (!this.Connected) {
				Connect();
			}

			// grab an encryption key for the password
			SetStatus("Requesting encryption key...");
			string EncryptionKey = GetEncryptionKey();
			
			// encrypt the entered password
			SetStatus("Encrypting password...");
			string EncryptedPassword = EncryptPassword(txtPassword.Text, EncryptionKey);

			// authenticate the username and password
			SetStatus("Authenticating username: " + txtUsername.Text + "...");
			try {
				Authenticate(txtUsername.Text, EncryptedPassword);
			} catch (System.Security.Authentication.AuthenticationException x) {
				SetStatus("Error: " + x.Message);
				ChangeState(State.Login, true, false);
				return;
			}

			// display the account name
			lblAccountName.Text = txtUsername.Text.ToUpper();

			// grab a list of games
			SetStatus("Retrieving game list...");
			_Games = GetGameList();

			// run through each game and get more information about each
			foreach (GameInfo Game in _Games) {
				SetStatus("Looking up game: " + Game.Name);
				Game.Type = GetGameType(Game.Code);
				Game.Subscription = GetSubscription(Game.Code);
			}
			
			// populate the games list
			PopulateGamesList();

			// move to the next state
			ChangeState(State.Game, true, true);
		}

		/// <summary>
		/// Populates lsvGames with the contents of _Games;
		/// </summary>
		private void PopulateGamesList() {
			// if we don't have any games yet then this is pointless
			if (_Games == null) {
				return;
			}

			// run through the list of games and populate the list view
			lsvGames.Items.Clear();
			foreach (GameInfo Game in _Games) {
				bool Allowed = true;
				if (chbAllowedGames.Checked) {
					// if they only want allowed games, check if this one is allowed
					if (Game.Subscription != Subscription.Normal && Game.Subscription != Subscription.Premium) {
						Allowed = false;
					}
				}

				// if this game is allowed, add it to the list
				if (Allowed) {
					ListViewItem LVI = new ListViewItem(new string[] { Game.Code, Game.Name, Game.Subscription.ToString() });
					if (Game.Code == _LastGame) {
						LVI.Selected = true;
					}
					lsvGames.Items.Add(LVI);
				}
			}
		}

		/// <summary>
		/// Called when the Checked state of the AllowedGames box is changed.
		/// </summary>
		/// <param name="Sender">The checkbox that was changed.</param>
		/// <param name="e">No arguments.</param>
		private void chbAllowedGames_CheckedChanged(object Sender, EventArgs e) {
			// re-populate the games list
			PopulateGamesList();
			
			// inform Black Lightning of the value change
			base.ChangeOptionValue(ShowAvailGamesOptionName, chbAllowedGames.Checked);
		}

		/// <summary>
		/// Called when an item in the games list is double-clicked.
		/// </summary>
		/// <param name="Sender">The ListView that had an item activated.</param>
		/// <param name="e">No arguments.</param>
		private void lsvGames_ItemActivate(object Sender, EventArgs e) {
			// start the move to the next state
			Next_Game();
		}

		/// <summary>
		/// Called to initiate the change from the Game state to the Login state.
		/// </summary>
		private void Back_Game() {
			// leave the game state
			ChangeState(State.Game, false, false);

			// enter the login state
			ChangeState(State.Login, true, true);
		}

		/// <summary>
		/// Called to initiate the change from the Game state to the Character state.
		/// </summary>
		private void Next_Game() {
			// leave the game state
			ChangeState(State.Game, false, false);

			// if we're already connected and have a list of characters, jump straight to the next state
			if (_Chars != null && Connected) {
				ChangeState(State.Character, true, true);
				return;
			}

			// make sure they selected a game
			if (lsvGames.SelectedItems.Count == 0) {
				SetStatus("You must select a game to continue.");
				ChangeState(State.Game, true, false);
				return;
			}

			// find the selected game
			string GameCode = lsvGames.SelectedItems[0].SubItems[0].Text;
			foreach (GameInfo Game in _Games) {
				if (Game.Code == GameCode) {
					_SelectedGame = Game;
					base.ChangeOptionValue(LastGameOptionName, GameCode);
					break;
				}
			}

			// select the game on the server
			SetStatus("Logging into game: " + _SelectedGame.Name + "...");
			SelectGame(_SelectedGame.Code);

			// grab a character list for the selected game
			SetStatus("Retrieving character list...");
			_Chars = GetCharacterList();
			
			// display the character name and slot information
			lblGameName.Text  = _SelectedGame.Name;
			lblCharSlots.Text = _Chars.SlotsUsed.ToString() + "/" + _Chars.SlotsTotal.ToString();

			// populate the character list
			PopulateCharacterList();

			// move to the next state
			ChangeState(State.Character, true, true);
		}

		/// <summary>
		/// Populates lsvChars with the contents of _Chars;
		/// </summary>
		private void PopulateCharacterList() {
			// run through the list of characters and populate the list view
			lsvChars.Items.Clear();
			foreach (KeyValuePair<string,string> Char in _Chars.Characters) {
				ListViewItem LVI = new ListViewItem(new string[]{Char.Value, Char.Key});
				if (Char.Key == _LastChar) {
					LVI.Selected = true;
				}
				lsvChars.Items.Add(LVI);
			}
		}

		/// <summary>
		/// Called when an item in the character list is double-clicked.
		/// </summary>
		/// <param name="Sender">The ListView that had an item activated.</param>
		/// <param name="e">No arguments.</param>
		private void lsvChars_ItemActivate(object Sender, EventArgs e) {
			// start the move to the next state
			Next_Character();
		}

		/// <summary>
		/// Called to initiate the change from the Character state to the Game state.
		/// </summary>
		private void Back_Character() {
			// leave the character state
			ChangeState(State.Character, false, false);

			// enter the game state
			ChangeState(State.Game, true, true);
		}

		/// <summary>
		/// Called to initiate the change from the Character state to executing the game.
		/// </summary>
		private void Next_Character() {
			// leave the character state
			ChangeState(State.Character, false, false);

			// find the selected character and get an SAL file for it
			string CharName = lsvChars.SelectedItems[0].SubItems[0].Text;
			_SelectedChar = lsvChars.SelectedItems[0].SubItems[1].Text;
			_SelectedFE   = rdbStormFront.Checked ? "STORM" : "WIZ";
			base.ChangeOptionValue(LastCharOptionName, _SelectedChar);
			base.ChangeOptionValue(LastFEOptionName,   _SelectedFE);
			_SalFile = GetSalFile(_SelectedChar, CharName, rdbStormFront.Checked ? global::BlackLightning.PluginLib.FrontEnd.StormFront : global::BlackLightning.PluginLib.FrontEnd.Wizard);
			
			// disconnect from the server
			SetStatus("Disconnecting...");
			Disconnect();

			// close the window with an OK result
			SetStatus("Launching game...");
			DialogResult = DialogResult.OK;
			Close();
		}
	}
}
